package com.googlecode.biglog.appender.mongodb;

import java.util.Map;

import net.sf.common.util.ValueUtils;

import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.dbist.annotation.Table;

import com.googlecode.biglog.appender.AbstractModelAppender;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;

public class MongoDbAppender extends AbstractModelAppender {
	private String url;
	private String writeConcern = null;
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getWriteConcern() {
		return writeConcern;
	}
	public void setWriteConcern(final String writeConcern) {
		this.writeConcern = writeConcern;
	}

	private String database;
	private MongoClient client;
	private DB db;
	private DBCollection collection;
	private WriteConcern concern;
	private boolean initialized = false;
	public WriteConcern getConcern() {
		if (concern == null)
			concern = collection.getWriteConcern();
		return concern;
	}
	@Override
	public boolean isInitialized() {
		return initialized && super.isInitialized();
	}

	@Override
	public void activateOptions() {
		super.activateOptions();

		try {
			close();

			if (ValueUtils.isEmpty(url))
				throw new IllegalArgumentException("url property is required.");
			if (!url.startsWith("mongodb://"))
				throw new IllegalArgumentException("url must strats with 'mongodb://': " + url);
			if (url.indexOf('/', 10) == -1)
				throw new IllegalArgumentException("url must have the database info: " + url);

			if (database == null) {
				database = url.substring(url.lastIndexOf('/') + 1);
				if (database.indexOf('?') != -1)
					database = database.substring(0, database.indexOf('?'));
			}

			client = new MongoClient(new MongoClientURI(url));

			db = client.getDB(database);

			String name;
			if (getModelClass() == null) {
				name = getModelName();
			} else {
				name = getModelClass().getSimpleName().toLowerCase();
				Table table = getModelClass().getAnnotation(Table.class);
				if (table != null && !ValueUtils.isEmpty(table.name()))
					name = table.name().toLowerCase();
			}
			collection = db.getCollection(name);
			if (collection == null)
				throw new IllegalArgumentException("Couldn't find collection(table): " + name);
			if (!ValueUtils.isEmpty(writeConcern))
				concern = WriteConcern.valueOf(writeConcern);

			initialized = true;

		} catch (Exception e) {
			errorHandler.error("Unexpected exception while initialising MongoDbAppender.", e, ErrorCode.GENERIC_FAILURE);
		}
	}

	@Override
	public void close() {
		collection = null;
		db = null;
		if (client == null)
			return;
		MongoClient mc = client;
		client = null;
		mc.close();
	}

	@Override
	protected void append(final LoggingEvent loggingEvent) {
		Map<String, Object> map = toMap(loggingEvent);
		if (map == null)
			return;

		DBObject log = new BasicDBObject(map);

		try {
			collection.insert(log, getConcern());
		} catch (MongoException e) {
			errorHandler.error("Failed to insert document to MongoDB", e, ErrorCode.WRITE_FAILURE);
		}
	}
}
